﻿using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Collections.Generic;

using Takkou.Language;

namespace Takkou.Core
{
    public class Mission
    {
        public World World { get; private set; }
        public string Name { get; private set; }

        public Mission(string fileName)
        {
            processFile(fileName);
        }

        // TODO a function to check player success
        // TODO functions for dynamic objects

        /** Parse the xml file and create the corresponding world */
        private void processFile(string fileName)
        {
            XDocument doc = XDocument.Load(fileName);
            if (doc.Root.Name != "mission")
                throw new MalformedMissionException("Root element is not <mission>");

            Name = doc.Root.Attribute("name").Value;

            loadWorld(doc);
            loadObjects(doc);


            /*
            XmlReader reader = XmlReader.Create(fileName);
            reader.MoveToContent();
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element)
                {
                    switch (reader.Name)
                    {
                        case "mission":
                            Name = reader.GetAttribute("name");
                            break;
                        case "world":
                            x = Int16.Parse(reader.GetAttribute("width"));
                            y = Int16.Parse(reader.GetAttribute("height"));
                            t = reader.ReadInnerXml();
                            World = new World(x, y, t);
                            break;
                        case "elements":
                            break;
                        case "robot":
                            id = reader.GetAttribute("id");
                            x = Int16.Parse(reader.GetAttribute("x"));
                            y = Int16.Parse(reader.GetAttribute("y"));
                            Dir dir = (Dir) Enum.Parse(typeof(Dir), reader.GetAttribute("dir"), true);
                            Robot rob = new RepeatRobot(id, x, y, dir);
                            World.addWorldElement(rob);
                            break;
                        case "object":
                            id = reader.GetAttribute("id");
                            x = Int16.Parse(reader.GetAttribute("x"));
                            y = Int16.Parse(reader.GetAttribute("y"));
                            t = reader.GetAttribute("type");
                            if (t == "key")
                            {
                                WorldElement elm = new WorldElement(id, ElmType.KEY)
                                {
                                    View = new Sprite(new Key())
                                    {
                                        X = x,
                                        Y = y,
                                        Direction = Dir.EAST
                                    }
                                };
                                World.addWorldElement(elm);
                            }
                            break;
                    }
                }
            }
             */
        }

        private Block readBlock(Robot bot, XElement behavior)
        {
            Block block = new Block();
            int count;

            foreach (XElement statement in behavior.Descendants())
            {
                switch (statement.Name.LocalName)
                {
                    case "move":
                        count = (int) statement.Attribute("count");
                        if (count > 1)
                            block.addStatement(bot.MOVE, count);
                        else
                            block.addStatement(bot.MOVE);
                        break;

                    case "left":
                        count = (int)statement.Attribute("count");
                        if (count > 1)
                            block.addStatement(bot.TURN_LEFT, count);
                        else
                            block.addStatement(bot.TURN_LEFT);
                        break;

                    case "right":
                        try
                        {
                            count = (int)statement.Attribute("count");
                            block.addStatement(bot.TURN_RIGHT, count);
                        }
                        catch (Exception e)
                        {
                            block.addStatement(bot.TURN_RIGHT);
                        }
                        break;
                }
            }
            return block;
        }

        private void loadObjects(XDocument doc)
        {
            int x, y;
            string id, t;

            XElement elements = doc.Root.Element("elements");
            foreach (XElement elm in elements.DescendantNodes())
            {
                id = elm.Attribute("id").Value;
                x = Int16.Parse(elm.Attribute("x").Value);
                y = Int16.Parse(elm.Attribute("y").Value);

                switch (elm.Name.LocalName)
                {
                    case "object":
                        t = elm.Attribute("type").Value;
                        if (t == "key")
                        {
                            WorldElement e = new WorldElement(id, ElmType.KEY)
                            {
                                View = new Sprite(new Key())
                                {
                                    X = x,
                                    Y = y,
                                    Direction = Dir.EAST
                                }
                            };
                            World.addWorldElement(e);
                        }

                        break;
                    case "robot":
                        Dir dir = (Dir)Enum.Parse(typeof(Dir), elm.Attribute("dir").Value, true);
                        Robot rob = new RepeatRobot(id, x, y, dir);

                        string behavior = (string) elm.Attribute("behavior");
                        if (behavior != null)
                        {
                            var beh = from b in doc.Root.Descendants("behavior")
                                      where b.Attribute("id").Value == behavior
                                      select b;
                            rob.plan = readBlock(rob, beh.First());
                        }

                        World.addWorldElement(rob);
                        break;
                }
            }
        }

        private void loadWorld(XDocument doc)
        {
            XElement world = doc.Root.Element("world");
            if (world == null)
            {
                throw new MalformedMissionException("<world> tag is missing");
            }
            int w = Int16.Parse(world.Attribute("width").Value);
            int h = Int16.Parse(world.Attribute("height").Value);
            string content = world.Value;
            World = new World(w, h, content);
        }
    }
}
